home *** CD-ROM | disk | FTP | other *** search
/ Aminet 28 / Aminet 28 (1998)(GTI - Schatztruhe)[!][Dec 1998].iso / Aminet / dev / c / qtools0.2-src.lha / src / libqbuild / writebsp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-07-15  |  15.9 KB  |  582 lines

  1. #define    LIBQBUILD_CORE
  2. #include "../include/libqbuild.h"
  3.  
  4. int headclipnode;                        /* 4 */
  5. int firstface;                            /* 4 */
  6.  
  7. int *planemapping;
  8.  
  9. /*=========================================================================== */
  10.  
  11. /*
  12.  * ==================
  13.  * FindFinalPlane
  14.  * 
  15.  * Used to find plane index numbers for clip nodes read from child processes
  16.  * ==================
  17.  */
  18. int FindFinalPlane(__memBase, struct dplane_t *p)
  19. {
  20.   int i;
  21.   struct dplane_t *dplane;
  22.  
  23.   for (i = 0, dplane = bspMem->shared.quake1.dplanes; i < bspMem->shared.quake1.numplanes; i++, dplane++) {
  24.     if (p->type != dplane->type)
  25.       continue;
  26.     if (p->dist != dplane->dist)
  27.       continue;
  28.     if (p->normal[0] != dplane->normal[0])
  29.       continue;
  30.     if (p->normal[1] != dplane->normal[1])
  31.       continue;
  32.     if (p->normal[2] != dplane->normal[2])
  33.       continue;
  34.     return i;
  35.   }
  36.  
  37.   /* new plane */
  38.   if (bspMem->shared.quake1.numplanes == bspMem->shared.quake1.max_numplanes)
  39.     ExpandClusters(bspMem, LUMP_PLANES);
  40.   dplane = &bspMem->shared.quake1.dplanes[bspMem->shared.quake1.numplanes];
  41.   *dplane = *p;
  42.   bspMem->shared.quake1.numplanes++;
  43.  
  44.   return bspMem->shared.quake1.numplanes - 1;
  45. }
  46.  
  47. void WriteNodePlanes_r(__memBase, register struct node *node)
  48. {
  49.   struct plane *plane;
  50.   struct dplane_t *dplane;
  51.  
  52.   if (node->planenum == -1)
  53.     return;
  54.   if (planemapping[node->planenum] == -1) {            /* a new plane */
  55.  
  56.     planemapping[node->planenum] = bspMem->shared.quake1.numplanes;
  57.  
  58.     if (bspMem->shared.quake1.numplanes == bspMem->shared.quake1.max_numplanes)
  59.       ExpandClusters(bspMem, LUMP_PLANES);
  60. #ifdef EXHAUSIVE_CHECK
  61.     if (node->planenum >= bspMem->numbrushplanes || node->planenum < 0)
  62.       Error("looking for nonexisting plane %d\n", node->planenum);
  63. #endif
  64.     plane = &bspMem->brushplanes[node->planenum];
  65.     dplane = &bspMem->shared.quake1.dplanes[bspMem->shared.quake1.numplanes];
  66.     dplane->normal[0] = plane->normal[0];
  67.     dplane->normal[1] = plane->normal[1];
  68.     dplane->normal[2] = plane->normal[2];
  69.     dplane->dist = plane->dist;
  70.     dplane->type = plane->type;
  71.  
  72.     bspMem->shared.quake1.numplanes++;
  73.   }
  74.  
  75.   node->outputplanenum = planemapping[node->planenum];
  76.  
  77.   WriteNodePlanes_r(bspMem, node->children[0]);
  78.   WriteNodePlanes_r(bspMem, node->children[1]);
  79. }
  80.  
  81. /*
  82.  * ==================
  83.  * WriteNodePlanes
  84.  * 
  85.  * ==================
  86.  */
  87. void WriteNodePlanes(__memBase, struct node *nodes)
  88. {
  89.   if (!(planemapping = (int *)tmalloc(sizeof(int) * bspMem->numbrushplanes)))
  90.       Error(failed_memoryunsize, "planemapping");
  91.   memset(planemapping, -1, sizeof(int) * bspMem->numbrushplanes);
  92.  
  93.   WriteNodePlanes_r(bspMem, nodes);
  94.   tfree(planemapping);
  95. }
  96.  
  97. /*=========================================================================== */
  98.  
  99. /*
  100.  * ==================
  101.  * WriteClipNodes_r
  102.  * 
  103.  * ==================
  104.  */
  105. int WriteClipNodes_r(__memBase, register struct node *node)
  106. {
  107.   int c;
  108.   short int i;
  109.   struct dclipnode_t *cn;
  110.   int num;
  111.  
  112.   /* FIXME: tfree more stuff?       */
  113.   if (node->planenum == -1) {
  114.     num = node->contents;
  115.     tfree(node);
  116.     return num;
  117.   }
  118.  
  119.   if (bspMem->shared.quake1.numclipnodes == bspMem->shared.quake1.max_numclipnodes)
  120.     ExpandClusters(bspMem, LUMP_CLIPNODES);
  121.   /* emit a clipnode */
  122.   c = bspMem->shared.quake1.numclipnodes;
  123.   cn = &bspMem->shared.quake1.dclipnodes[c];
  124.   bspMem->shared.quake1.numclipnodes++;
  125.  
  126.   cn->planenum = node->outputplanenum;
  127.   for (i = 0; i < 2; i++)
  128.     cn->children[i] = WriteClipNodes_r(bspMem, node->children[i]);
  129.  
  130.   tfree(node);
  131.   return c;
  132. }
  133.  
  134. /*
  135.  * ==================
  136.  * WriteClipNodes
  137.  * 
  138.  * Called after the clipping hull is completed.  Generates a disk format
  139.  * representation and frees the original memory.
  140.  * ==================
  141.  */
  142. void WriteClipNodes(__memBase, struct node *nodes)
  143. {
  144.   headclipnode = bspMem->shared.quake1.numclipnodes;
  145.   WriteClipNodes_r(bspMem, nodes);
  146. }
  147.  
  148. /*=========================================================================== */
  149.  
  150. /*
  151.  * ==================
  152.  * WriteLeaf
  153.  * ==================
  154.  */
  155. void WriteLeaf(__memBase, register struct node *node)
  156. {
  157.   struct visfacet **fp, *f;
  158.   struct dleaf_t *leaf_p;
  159.  
  160.   /* emit a leaf */
  161.   if (bspMem->shared.quake1.numleafs == bspMem->shared.quake1.max_numleafs)
  162.     ExpandClusters(bspMem, LUMP_LEAFS);
  163.   leaf_p = &bspMem->shared.quake1.dleafs[bspMem->shared.quake1.numleafs];
  164.   bspMem->shared.quake1.numleafs++;
  165.  
  166.   leaf_p->contents = node->contents;
  167.  
  168.   /* write bounding box info */
  169.   VectorCopy(node->mins, leaf_p->mins);
  170.   VectorCopy(node->maxs, leaf_p->maxs);
  171.  
  172.   leaf_p->visofs = -1;                        /* no vis info yet */
  173.  
  174.   /* write the marksurfaces */
  175.   leaf_p->firstmarksurface = bspMem->shared.quake1.nummarksurfaces;
  176.  
  177.   for (fp = node->markfaces; *fp; fp++) {
  178.     /* emit a marksurface */
  179.     f = *fp;
  180.     do {
  181.       if (bspMem->shared.quake1.nummarksurfaces == bspMem->shared.quake1.max_nummarksurfaces)
  182.     ExpandClusters(bspMem, LUMP_MARKSURFACES);
  183.       bspMem->shared.quake1.dmarksurfaces[bspMem->shared.quake1.nummarksurfaces] = f->outputnumber;
  184.       bspMem->shared.quake1.nummarksurfaces++;
  185.       f = f->original;                        /* grab tjunction split faces */
  186.     } while (f);
  187.   }
  188.  
  189.   leaf_p->nummarksurfaces = bspMem->shared.quake1.nummarksurfaces - leaf_p->firstmarksurface;
  190. }
  191.  
  192. /*
  193.  * ==================
  194.  * WriteDrawNodes_r
  195.  * ==================
  196.  */
  197. void WriteDrawNodes_r(__memBase, register struct node *node)
  198. {
  199.   struct dnode_t *n;
  200.   short int i;
  201.  
  202.   /* emit a node   */
  203.   if (bspMem->shared.quake1.numnodes == bspMem->shared.quake1.max_numnodes)
  204.     ExpandClusters(bspMem, LUMP_NODES);
  205.   n = &bspMem->shared.quake1.dnodes[bspMem->shared.quake1.numnodes];
  206.   bspMem->shared.quake1.numnodes++;
  207.  
  208.   VectorCopy(node->mins, n->mins);
  209.   VectorCopy(node->maxs, n->maxs);
  210.  
  211.   n->planenum = node->outputplanenum;
  212.   n->firstface = node->firstface;
  213.   n->numfaces = node->numfaces;
  214.  
  215.   /* recursively output the other nodes */
  216.   for (i = 0; i < 2; i++) {
  217.     if (node->children[i]->planenum == -1) {
  218.       if (node->children[i]->contents == CONTENTS_SOLID)
  219.     n->children[i] = -1;
  220.       else {
  221.     n->children[i] = -(bspMem->shared.quake1.numleafs + 1);
  222.     WriteLeaf(bspMem, node->children[i]);
  223.       }
  224.     }
  225.     else {
  226.       n->children[i] = bspMem->shared.quake1.numnodes;
  227.       WriteDrawNodes_r(bspMem, node->children[i]);
  228.     }
  229.   }
  230. }
  231.  
  232. /*
  233.  * ==================
  234.  * WriteDrawNodes
  235.  * ==================
  236.  */
  237. void WriteDrawNodes(__memBase, struct node *headnode)
  238. {
  239.   short int i;
  240.   int start;
  241.   struct dmodel_t *bm;
  242.  
  243. #if 0
  244.   if (headnode->contents < 0)
  245.     Error("FinishBSPModel: empty model");
  246. #endif
  247.  
  248.   /* emit a model */
  249.   if (bspMem->shared.quake1.nummodels == bspMem->shared.quake1.max_nummodels)
  250.     ExpandClusters(bspMem, LUMP_MODELS);
  251.   bm = &bspMem->shared.quake1.dmodels[bspMem->shared.quake1.nummodels];
  252.   bspMem->shared.quake1.nummodels++;
  253.  
  254.   bm->headnode[0] = bspMem->shared.quake1.numnodes;
  255.   bm->firstface = firstface;
  256.   bm->numfaces = bspMem->shared.quake1.numfaces - firstface;
  257.   firstface = bspMem->shared.quake1.numfaces;
  258.  
  259.   start = bspMem->shared.quake1.numleafs;
  260.  
  261.   if (headnode->contents < 0)
  262.     WriteLeaf(bspMem, headnode);
  263.   else
  264.     WriteDrawNodes_r(bspMem, headnode);
  265.   bm->visleafs = bspMem->shared.quake1.numleafs - start;
  266.  
  267.   for (i = 0; i < 3; i++) {
  268.     bm->mins[i] = headnode->mins[i] + SIDESPACE + 1;        /* remove the padding */
  269.     bm->maxs[i] = headnode->maxs[i] - SIDESPACE - 1;
  270.   }
  271.   /* FIXME: are all the children decendant of padded nodes? */
  272. }
  273.  
  274. /*
  275.  * ==================
  276.  * BumpModel
  277.  * 
  278.  * Used by the clipping hull processes that only need to store headclipnode
  279.  * ==================
  280.  */
  281. void BumpModel(__memBase, int hullnum)
  282. {
  283.   struct dmodel_t *bm;
  284.  
  285.   /* emit a model */
  286.   if (bspMem->shared.quake1.nummodels == bspMem->shared.quake1.max_nummodels)
  287.     ExpandClusters(bspMem, LUMP_MODELS);
  288.   bm = &bspMem->shared.quake1.dmodels[bspMem->shared.quake1.nummodels];
  289.   bspMem->shared.quake1.nummodels++;
  290.  
  291.   bm->headnode[hullnum] = headclipnode;
  292. }
  293.  
  294. /*============================================================================= */
  295.  
  296. #define    MAX_MULTIPLE    32
  297.  
  298. /*
  299.  * ==================
  300.  * WriteMiptex
  301.  * ==================
  302.  */
  303. void WriteMiptex(__memBase)
  304. {
  305.   if (!(bspMem->bspOptions & QBSP_NOTEXTURES)) {
  306.     struct wadheader Header[MAX_MULTIPLE + 1];
  307.     struct wadentry *allEntries[MAX_MULTIPLE + 1];
  308.     char *wadPath[MAX_MULTIPLE + 1];
  309.     HANDLE wadFile[MAX_MULTIPLE + 1];
  310.     int wadAvail = 0;
  311.  
  312.     char *dirPath[MAX_MULTIPLE + 1];
  313.     DIR *dirDir[MAX_MULTIPLE + 1];
  314.     int dirAvail = 0;
  315.  
  316.     /* TODO: multiple wadFiles */
  317.     if (bspMem->mapentities) {
  318.       wadPath[0] = ValueForKey(&bspMem->mapentities[0], "_wad");
  319.       if (!wadPath[0] || !wadPath[0][0])
  320.     wadPath[0] = ValueForKey(&bspMem->mapentities[0], "wad");
  321.     }
  322.     else
  323.       wadPath[0] = 0;
  324.     if (!wadPath[0] || !wadPath[0][0])
  325.       wadPath[0] = getenv("QUAKE_WADFILE");
  326.  
  327.     if (wadPath[0] && wadPath[0][0]) {                /* do only if there exists really a wad */
  328.       for (wadAvail = 0; wadAvail < MAX_MULTIPLE;) {
  329.     char *hit;
  330.  
  331.     if ((hit = (char *)__strchr(wadPath[wadAvail], ';'))) {    /* cut off next entry */
  332.       *hit = ' ';
  333.       while ((hit[-1] == ' ') || (hit[-1] == '\t'))        /* delete whitespace */
  334.         hit--;
  335.       *hit++ = '\0';
  336.       while ((*hit == ' ') || (*hit == '\t'))        /* delete whitespace */
  337.         hit++;
  338.     }
  339. #ifdef DEBUG
  340.     printf("wadPath %2d: \"%s\"\n", wadAvail, wadPath[wadAvail]);
  341. #endif
  342.  
  343.     if ((wadPath[wadAvail][0]) && (wadFile[wadAvail] = __open(wadPath[wadAvail], H_READ_BINARY)) > 0) {
  344.       if (CheckWAD2(wadFile[wadAvail], &Header[wadAvail], FALSE)) {
  345.         FindWAD2(wadFile[wadAvail], 0, &Header[wadAvail], &allEntries[wadAvail], 0);
  346.         wadAvail++;
  347.       }
  348.       else {
  349.         __close(wadFile[wadAvail]);
  350.         eprintf("file \"%s\" not a wad\n", wadPath[wadAvail]);
  351.       }
  352.     }
  353.     else
  354.       eprintf(failed_fileopen, wadPath[wadAvail]);
  355.  
  356.     if (!hit)                        /* break if nothing more found */
  357.       break;
  358.     else
  359.       wadPath[wadAvail] = hit;                /* register next entry */
  360.       }
  361.     }
  362.  
  363.     /* TODO: multiple dirDirs */
  364.     if (bspMem->mapentities) {
  365.       dirPath[0] = ValueForKey(&bspMem->mapentities[0], "_dir");
  366.       if (!dirPath[0] || !dirPath[0][0])
  367.     dirPath[0] = ValueForKey(&bspMem->mapentities[0], "dir");
  368.     }
  369.     else
  370.       dirPath[0] = 0;
  371.     if (!dirPath[0] || !dirPath[0][0])
  372.       dirPath[0] = getenv("QUAKE_WADDIR");
  373.     if (!dirPath[0] || !dirPath[0][0])
  374.       dirPath[0] = "\0";
  375.  
  376.     for (dirAvail = 0; dirAvail < MAX_MULTIPLE;) {        /* do ever, 'cause we have ever a valid dir (current dir) */
  377.       char *hit;
  378.  
  379.       if ((hit = (char *)__strchr(dirPath[dirAvail], ';'))) {    /* cut off next entry */
  380.     *hit = ' ';
  381.     while ((hit[-1] == ' ') || (hit[-1] == '\t'))        /* delete whitespace */
  382.       hit--;
  383.     *hit++ = '\0';
  384.     while ((*hit == ' ') || (*hit == '\t'))            /* delete whitespace */
  385.       hit++;
  386.       }
  387. #ifdef DEBUG
  388.       printf("dirPath %2d: \"%s\"\n", dirAvail, dirPath[dirAvail]);
  389. #endif
  390.  
  391.       if ((dirDir[dirAvail] = opendir(dirPath[dirAvail])))
  392.     dirAvail++;                        /* skip unavailable entries */
  393.       else
  394.     eprintf("dir \"%s\" is not a dir, or does not exists\n", dirPath[dirAvail]);
  395.  
  396.       if (!hit)                            /* break if nothing more found */
  397.     break;
  398.       else
  399.     dirPath[dirAvail] = hit;                /* register next entry */
  400.     }
  401. #ifdef DEBUG
  402.     printf("wads %2d, dirs %2d\n", wadAvail, dirAvail);
  403. #endif
  404.  
  405.     if ((wadAvail || dirAvail) && bspMem->nummaptexstrings) {
  406.       int i;
  407.       unsigned char *mipFlow;
  408.       struct dmiptexlump_t *mipBlock;
  409.       int texstrings = bspMem->nummaptexstrings;
  410.  
  411.       if (!bspMem->shared.quake1.dtexdata)
  412.     AllocClusters(bspMem, LUMP_TEXTURES);
  413.       mipBlock = (struct dmiptexlump_t *)bspMem->shared.quake1.dtexdata;
  414.       mipFlow = (unsigned char *)&mipBlock->dataofs[bspMem->nummaptexstrings];
  415.       mipBlock->nummiptex = bspMem->nummaptexstrings;
  416.       bspMem->shared.quake1.texdatasize = mipFlow - bspMem->shared.quake1.dtexdata;
  417.  
  418.       for (i = 0; i < texstrings; i++) {
  419.     struct rawdata *inPut;
  420.  
  421.     void GetInput(char *inName) {
  422.       int j;
  423.       struct wadentry *Entry;
  424.  
  425.         inPut = 0;
  426.  
  427.       /* first search in wadFiles */
  428.       for (j = 0; j < wadAvail; j++)
  429.         if ((Entry = SearchWAD2(inName, &Header[j], allEntries[j], TYPE_MIPMAP)))
  430.             inPut = GetWAD2Raw(wadFile[j], Entry);
  431.  
  432.       /* if nothing found, search in dirDirs */
  433.       for (j = 0; (j < dirAvail) && !(inPut); j++) {
  434.         struct dirent *dirEnt = 0;
  435.  
  436.         while ((dirEnt = readdir(dirDir[j]))) {
  437. #ifdef DEBUG
  438.           printf("dirname: %s wadname: %s\n", dirEnt->d_name, inName);
  439. #endif
  440.           if (!__strncasecmp(dirEnt->d_name, inName, strlen(inName)))    /* metal1 matches metal10* */
  441.         if (dirEnt->d_name[strlen(inName)] == '.')    /* metal1 matches metal1.* */
  442.           break;
  443.         } if (dirEnt) {
  444.           char *fileExt = GetExt(dirEnt->d_name);
  445.           FILE *inFile;
  446.           struct palpic *inPic = 0;
  447.           char *fileName;
  448.  
  449.           if ((fileName = (char *)tmalloc(NAMELEN_PATH + 1))) {
  450.         __strncpy(fileName, dirPath[j], NAMELEN_PATH);
  451.         __strncat(fileName, "/", NAMELEN_PATH);
  452.         __strncat(fileName, dirEnt->d_name, NAMELEN_PATH);
  453.  
  454.         if ((inFile = __fopen(fileName, F_READ_BINARY))) {
  455.           if (!__strcmp(fileExt, "mip"))
  456.             inPut = GetRaw(fileno(inFile), inName, 0);
  457.           else if (!__strcmp(fileExt, "lmp"))
  458.             inPic = GetLMP(fileno(inFile), inName);
  459.           else {
  460.             short int alignX = 16, alignY = 16;
  461.  
  462.             if (fileName[0] == WARP_MIPMAP)
  463.               alignX = alignY = WARP_X;
  464.             else if (!__strncasecmp(fileName, SKY_MIPMAP, 3)) {
  465.               alignX = -(SKY_X);
  466.               alignY = -(SKY_Y);
  467.             }
  468.             inPic = GetImage(inFile, inName, alignX, alignY);
  469.           }
  470.  
  471.           if (inPic) {
  472.             if ((inPut = rmalloc(MIP_MULT(inPic->width * inPic->height) + sizeof(struct mipmap), inName)))
  473.                 PasteMipMap((struct mipmap *)inPut->rawdata, inPic);
  474.  
  475.             pfree(inPic);
  476.           }
  477.           else
  478.             eprintf("unknown fileformat %s\n", fileName);
  479.  
  480.           __fclose(inFile);
  481.         }
  482.         else
  483.           eprintf(failed_fileopen, fileName);
  484.  
  485.         tfree(fileName);
  486.           }
  487.         }
  488.  
  489.         rewinddir(dirDir[j]);
  490.       }
  491.     }
  492.  
  493.     void PutInput(char *inName) {
  494.       if (inPut) {
  495.         mipBlock->dataofs[i] = mipFlow - (unsigned char *)mipBlock;
  496.         mprintf("    - load texture %s\n", inPut->name);
  497.  
  498.         if ((bspMem->shared.quake1.texdatasize + inPut->size) >= bspMem->shared.quake1.max_texdatasize) {
  499.           ExpandClusters(bspMem, LUMP_TEXTURES);
  500.           mipBlock = (struct dmiptexlump_t *)bspMem->shared.quake1.dtexdata;
  501.           mipFlow = bspMem->shared.quake1.dtexdata + bspMem->shared.quake1.texdatasize;
  502.         } bspMem->shared.quake1.texdatasize += inPut->size;
  503.  
  504.         memcpy(mipFlow, inPut->rawdata, inPut->size);
  505.         mipFlow += inPut->size;
  506.         rfree(inPut);
  507.       }
  508.       else {
  509.         mipBlock->dataofs[i] = -1;
  510.         eprintf("texture %s not found!\n", inName);
  511.       }
  512.     }
  513.  
  514.     if (bspMem->maptexstrings[i][0] == '+') {
  515.       int j;
  516.       char name[20];
  517.  
  518.       __strcpy(name, bspMem->maptexstrings[i]);
  519.  
  520.       for (j = 0; j < 20; j++) {
  521.         if (j < 10)
  522.           name[1] = '0' + j;
  523.         else
  524.           name[1] = 'A' + j - 10;                /* alternate animation */
  525.  
  526.         GetInput(name);                    /* see if this name exists in the wadfile */
  527.         PutInput(name);                    /* put only if it exists */
  528.         /*FindMiptex(bspMem, name);                                         // add to the miptex list, if somebody after us needs it */
  529.       }
  530.     }
  531.     else {
  532.       GetInput(bspMem->maptexstrings[i]);
  533.       PutInput(bspMem->maptexstrings[i]);
  534.     }
  535.       }
  536.  
  537.       for (i = 0; i < wadAvail; i++)
  538.     __close(wadFile[i]);
  539.       for (i = 0; i < dirAvail; i++)
  540.     closedir(dirDir[i]);
  541.     }
  542.     else
  543.       eprintf(failed_fileopen, "wadfile(s) or dir(s)");
  544.   }
  545. }
  546.  
  547. /*=========================================================================== */
  548.  
  549. /*
  550.  * ==================
  551.  * BeginBSPFile
  552.  * ==================
  553.  */
  554. void BeginBSPFile(__memBase)
  555. {
  556.   bspMem->availHeaders = 0;
  557.   AllocClusters(bspMem, ((ALL_QUAKE1_LUMPS) | (ALL_MAPS)) & (~((LUMP_LIGHTING) | (LUMP_VISIBILITY))));
  558.  
  559.   /* edge 0 is not used, because 0 can't be negated */
  560.   bspMem->shared.quake1.numedges = 1;
  561.  
  562.   /* leaf 0 is common solid with no faces */
  563.   bspMem->shared.quake1.numleafs = 1;
  564.   bspMem->shared.quake1.dleafs[0].contents = CONTENTS_SOLID;
  565.  
  566.   firstface = 0;
  567. }
  568.  
  569. /*
  570.  * ==================
  571.  * FinishBSPFile
  572.  * ==================
  573.  */
  574. void FinishBSPFile(__memBase, HANDLE bspFile)
  575. {
  576.   mprintf("----- FinishBSPFile -----\n");
  577.  
  578.   WriteMiptex(bspMem);
  579.   WriteBSP(bspFile, bspMem, BSP_VERSION_Q1);
  580.   PrintClusters(bspMem, 0, TRUE);
  581. }
  582.